/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */

/**
 * 参考 ExtJs4 提供的 Ext.ux.TreePicker 修改而来的 TreePicker, 下拉树
 *
 * var store = Ext.create('Ext.data.TreeStore', {
 *        idProperty: 'id',
 *        fields: ['name'],
 *        root: {
 *            name: 'XX',
 *            expanded: false,
 *            children: [
 *                {
 *                    id: '1',
 *                    name: '未分类',
 *                    checked: false,   // 使用多选必须需要, 单选不需要
 *                    leaf: true
 *                },
 *                {
 *                    id: '2',
 *                    name: '制动系统',
 *                    checked: false,
 *                    children: [
 *                        {
 *                            id: '2-1',
 *                            name: '传动器',
 *                            checked: false,
 *                            children: [
 *                                {
 *                                    id: '2-1-1',
 *                                    name: '那个神马',
 *                                    checked: false,
 *                                    leaf: true
 *                                },
 *                                {
 *                                    id: '2-1-2',
 *                                    name: '很长',
 *                                    checked: false,
 *                                    children: [
 *                                        {
 *                                            id: '5-3-1',
 *                                            name: '真的很长很长很长很长很长很长很长很长很长很长很长很长很长很长',
 *                                            checked: false,
 *                                            leaf: true
 *                                        }
 *                                    ]
 *                                }
 *                            ]
 *                        }
 *                    ]
 *                }
 *            ]
 *        }
 *  });
 *
 *  单选树配置:
 *
 *  {
 *    name: 'sort',
 *    xtype: 'treepicker',
 *    store: store,
 *
 *    value: ['2'],             // 默认选择值
 *    fieldLabel: '单选下拉树',
 *    displayField: 'name',
 *    selectLeafOnly: false,    // 是否只允许选择叶子节点
 *    rootVisible: true
 *  }
 *  多选树配置: (数据需要 checked)
 *  {
 *    name: 'sort',
 *    xtype: 'treepicker',
 *    multiSelect: true,
 *    store: store,
 *
 *    fieldLabel: '多选下拉树',
 *    displayField: 'name'
 *  }
 *
 *  @author vacoor
 */
Ext.define('Nothing.ux.form.TreePicker', {
    extend: 'Ext.form.field.Picker',
    xtype: 'treepicker',
    uses: ['Ext.tree.Panel'],

    config: {
        /**
         * @cfg {Ext.data.TreeStore} store
         * A tree store that the tree picker will be bound to
         */
        store: null,

        /**
         * @cfg {String} displayField
         * The field inside the model that will be used as the node's text.
         * Defaults to the default value of {@link Ext.tree.Panel}'s `displayField` configuration.
         */
        displayField: Ext.tree.Panel.prototype.displayField,

        /**
         * @cfg {Array} columns
         * An optional array of columns for multi-column trees
         */
        columns: null,

        /**
         * @cfg {Boolean} rootVisible
         * {@link Ext.tree.Panel} 的 rootVisible 属性, 默认 false
         */
        rootVisible: false,

        /**
         * @cfg {String} multiSelect
         *
         * 选择类型
         *   multiSelect = true: 多选模式, 使用 tree store 中 checked=true 的所有项作为值
         *   multiSelect = false: 单选模式, 即使tree store 含有 checked=true 也忽略
         */
        multiSelect: false,

        /**
         * 多选时显示的分隔符
         * multiSelect, delimiter 属性是为了和 ComboBox 一致, 方便在 rowediting 对多选处理
         */
        delimiter: ', ',

        /**
         * @cfg {Boolean} selectLeafOnly
         * 是否只能选择叶子节点, 当 multiSelect = true 时有效
         */
        selectLeafOnly: true,

        maxPickerWidth: 500,

        minPickerWidth: 10,
        /**
         * @cfg {Number} maxPickerHeight
         * The maximum height of the tree dropdown. Defaults to 500.
         */
        maxPickerHeight: 500,

        /**
         * @cfg {Number} minPickerHeight
         * The minimum height of the tree dropdown. Defaults to 50.
         */
        minPickerHeight: 10

    },

    editable: false,

    initComponent: function () {
        var me = this;
        me.callParent(arguments);
        me.addEvents(
            /**
             * @event select
             * Fires when decode tree node is selected
             * @param {Ext.ux.TreePicker} picker        This tree picker
             * @param {Ext.data.Model} record           The selected record
             */
            'select'
        );
    },

    /**
     * {@link Ext.form.field.Picker#createPicker}
     * @protected
     * @Override
     */
    createPicker: function () {
        var me = this,
            picker = new Ext.tree.Panel({
                rootVisible: me.rootVisible,
                displayField: me.displayField,
                columns: me.columns,
                store: me.store,

                maxWidth: me.maxPickerWidth,
                minWidth: me.minPickerWidth,
                maxHeight: me.maxPickerHeight,
                minHeight: me.minPickerHeight,

                shadow: false,
                floating: true,
                scroll: 'none', // 直接禁用, 否则调整大小后在IE下依然会出现滚动条
                shrinkWrapDock: 3,
                listeners: {
                    scope: me,
                    itemclick: me.onItemClick2,
                    itemexpand: me.onItemExpand,
                    itemcollapse: me.onItemExpand
                },
                viewConfig: {
                    listeners: {
                        scope: me,
                        itemkeydown: me.onItemClick2
                    }
                }
            }),
            view = picker.getView();

        //todo 增加一个全选..
        /*
         picker.on('checkchange', function (node, checked) {
         //node.expand();
         if (null != node.get('checked')) {
         node.set('checked', checked);
         }

         node.eachChild(function (child) {
         picker.fireEvent('checkchange', child, checked);
         });

         me.expand();
         });
         */

        // TODO
        if (Ext.isIE9 && Ext.isStrict) {
            // In IE9 strict mode, the tree view grows by the height of the horizontal scroll bar when the items are highlighted or unhighlighted.
            // Also when items are collapsed or expanded the height of the view is off. Forcing decode repaint fixes the problem.
            view.on({
                scope: me,
                highlightitem: me.repaintPickerView,
                unhighlightitem: me.repaintPickerView,
                afteritemexpand: me.repaintPickerView,
                afteritemcollapse: me.repaintPickerView
            });
        }
        return picker;
    },

    /**
     * {@link Ext.form.field.Picker#expand}
     * {@link Ext.form.field.Picker#alignPicker}
     * @protected
     * @override
     */
    alignPicker: function () {
        var me = this;

        me.callParent(arguments);

        if (me.isExpanded) {
            var picker = me.picker,
                view = picker.view,
                width = view.el.down(view.cellSelector).getWidth() + 2,
                x = Ext.getBody().getWidth() - width; // 右边界时x坐标

            // 修正 picker 宽度
            picker.setWidth(width);

            // 修正右侧超出body, (上下处理了, 但是左右没处理)
            if (picker.getX() > x) {
                picker.setX(x);
            }
        }
    },

    /**
     * {@link Ext.form.field.Picker#expand}
     * @override
     * @protected
     */
    onExpand: function () {
        var me = this,
            picker = me.picker,
            view = picker.view,
            store = picker.store,
            root = store.getRootNode(),
            node;

        if (!Ext.isArray(me.value)) {
            me.value = [me.value];
        }

        node = store.getNodeById(me.value[me.value.length - 1]) || root;

        // 多选 checkbox
        root.cascadeBy(function (n) {
            if (null != n.get('checked')) {
                // IE8 不支持 array.indexOf
                n.set('checked', -1 !== Ext.Array.indexOf(me.value, n.getId()));
            }
        });

        // 焦点选中已选择
        picker.selectPath(node.getPath());
        Ext.defer(Ext.bind(view.focus, view), 1);
    },

    /**
     * @protected
     * @override
     */
    onCollapse: function () {
        var me = this;

        if (me.multiSelect) {
            me.onItemSelected(me.picker.getChecked());
        }
    },

    /**
     * @private
     */
    onItemClick2: function (view, record, item, rowIndex, e) {
        var me = this,
            keydown = 'keydown' === e.type,
            checked;

        // 键盘事件
        if (keydown && e.ENTER !== e.getKey()) {
            return;
        }

        // view should tree view ( event: select --> click --> checked )
        if (e.getTarget(view.expanderSelector, item)) {
            return;
        }

        if (!me.multiSelect) {
            if (!me.selectLeafOnly || record.isLeaf()) {
                me.onItemSelected(record);
            } else if (record.isExpandable()) {
                record.isExpanded() ? record.collapse() : record.expand();
            }
        } else if (null != (checked = record.get('checked')) && !keydown) {
            // 多选且存在 checked 属性
            record.set('checked', !checked);
        }
    },

    /**
     * @private
     */
    onItemSelected: function (records) {
        var me = this,
            value = [];

        if (!Ext.isArray(records)) {
            records = [records];
        }

        Ext.each(records, function (rec) {
            value.push(rec.getId());
        });

        me.setValue(value);
        me.collapse();
        me.fireEvent('select', me, records)
    },

    /**
     * @private
     */
    // 展开或折叠树时重新布局, 延迟下执行, 否则没有效果
    onItemExpand: function () {
        Ext.defer(Ext.bind(this.alignPicker, this), 1)
    },

    /**
     * 不能调用parent, 否则在展开 tree 时会触发 picker 隐藏
     * @override
     */
    triggerBlur: Ext.emptyFn,

    /**
     * repaints the tree view, TODO 搞毛线？？？
     */
    repaintPickerView: function () {
        var style = this.picker.getView().getEl().dom.style;

        // can't use Element.repaint because it contains decode setTimeout, which results in decode flicker effect
        style.display = style.display;
    },


    // -------------- 表单方法
    /**
     */
    setValue: function (ids) {
        var me = this,
            store = me.store,
            display = [];

        if (!Ext.isArray(ids)) {
            ids = [ids];
        }

        me.value = ids;

        Ext.each(ids, function (id) {
            var record = store.getNodeById(id);
            if (record) {
                display.push(record.get(me.displayField) || record.get('text') || '');
            }
        });

        me.setRawValue(display.join(me.delimiter));

        return me;
    },

    /**
     * @override
     */
    getValue: function () {
        var me = this;

        if (!Ext.isArray(me.value)) {
            me.value = [me.value];
        }

        return 'MULTI' === me.multiSelect ? me.value : me.value[me.value.length - 1] || '';
    },

    /**
     * @override
     */
    getSubmitValue: function () {
        return this.getValue();
    }
});

